#include <string>
#include "video_parser.h"
extern "C"
{
    #include <libavcodec/avcodec.h>
}
#include <stdlib.h>

VideoParser::VideoParser(std::string file_name):filename(file_name)
{
    inbuf = new uint8_t[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    buf = new char[1024];
    pkt = av_packet_alloc();
    if (!pkt)
    {
        fprintf(stderr, "av_packet_alloc error\n");
        exit(1);
    }
    frame = av_frame_alloc();
    if (!frame) {
        fprintf(stderr, "Could not allocate video frame\n");
        exit(1);
    }
    
}

void VideoParser::read_file()
{
    int video_stream_idx = 0;
    AVStream *video_stream;
    if (avformat_open_input(&fmt_ctx, filename.c_str(), nullptr, nullptr) < 0) 
    {
        fprintf(stderr, "Could not open source file %s\n", filename.c_str());
        exit(1);
    }
    if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) 
    {
        fprintf(stderr, "Could not find stream information\n");
        exit(1);
    }
    //找到对应的video
    if (open_codec_context(&video_stream_idx, &c, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
        video_stream = fmt_ctx->streams[video_stream_idx];
    }

    while (av_read_frame(fmt_ctx, pkt) >= 0)
    {
        if (pkt->stream_index == video_stream_idx)
            process();
    }
    

}
VideoParser::~VideoParser()
{
    delete[] inbuf;
    delete[] buf;
    av_parser_close(parser);
    avcodec_free_context(&c);
    avformat_close_input(&fmt_ctx);
    av_frame_free(&frame);
    av_packet_free(&pkt);
}

void VideoParser::setCallBack(frame_callback callback)
{
    this->callback = callback;
}
void VideoParser::decode()
{
    read_file();
}
void VideoParser::process()
{
    int ret = 0;
    ret = avcodec_send_packet(c, pkt);
    if (ret < 0) 
    {
        fprintf(stderr, "Error sending a packet for decoding\n");
        exit(1);
    }

    while (ret >= 0) {
        ret = avcodec_receive_frame(c, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            exit(1);
        }
        fflush(stdout);
 
        /* the picture is allocated by the decoder. no need to
           free it */
        callback(frame->data[0], frame->linesize[0],
                 frame->width, frame->height);
    }
}


int VideoParser::open_codec_context(int *stream_idx,
                              AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)
{
        int ret, stream_index;
        AVStream *st;
    
        ret = av_find_best_stream(fmt_ctx, type, -1, -1, nullptr, 0);
        if (ret < 0) 
        {
            fprintf(stderr, "Could not find %s stream in input file '%s'\n",
                    av_get_media_type_string(type), filename.c_str());
            return ret;
        } 
        else 
        {
            stream_index = ret;
            st = fmt_ctx->streams[stream_index];
    
            /* find decoder for the stream AND set to codec */
            codec = avcodec_find_decoder(st->codecpar->codec_id);
            if (!codec) 
            {
                fprintf(stderr, "Failed to find %s codec\n",
                        av_get_media_type_string(type));
                return AVERROR(EINVAL);
            }
    
            /* Allocate a codec context for the decoder */
            *dec_ctx = avcodec_alloc_context3(codec);
            if (!*dec_ctx) 
            {
                fprintf(stderr, "Failed to allocate the %s codec context\n",
                        av_get_media_type_string(type));
                return AVERROR(ENOMEM);
            }
    
            /* Copy codec parameters from input stream to output codec context */
            if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) 
            {
                fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",
                        av_get_media_type_string(type));
                return ret;
            }
    
            /* Init the decoders */
            if ((ret = avcodec_open2(*dec_ctx, codec, nullptr)) < 0) 
            {
                fprintf(stderr, "Failed to open %s codec\n",
                        av_get_media_type_string(type));
                return ret;
            }
            *stream_idx = stream_index;
        }
    
        return 0;
    } 